package cn.cleanarch.infra.idp.s3.service.impl;

import cn.cleanarch.infra.idp.s3.domain.entity.S3Config;
import cn.cleanarch.infra.idp.s3.service.S3Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3ClientBuilder;
import software.amazon.awssdk.services.s3.model.*;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.time.Duration;

/**
 * S3服务实现类
 */
@Service
public class S3ServiceImpl implements S3Service {

    private static final Logger logger = LoggerFactory.getLogger(S3ServiceImpl.class);

    @Override
    public String uploadFile(S3Config config, MultipartFile file, String objectKey) {
        try {
            return uploadFile(config, file.getInputStream(), objectKey, file.getContentType());
        } catch (IOException e) {
            throw new RuntimeException("文件上传失败", e);
        }
    }

    @Override
    public String uploadFile(S3Config config, InputStream inputStream, String objectKey, String contentType) {
        S3Client s3Client = createS3Client(config);

        // 构建带基础路径的对象键
        String fullObjectKey = buildFullObjectKey(config, objectKey);

        try {
            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(config.getBucket())
                    .key(fullObjectKey)
                    .contentType(contentType)
                    .build();

            s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(inputStream, inputStream.available()));

            return fullObjectKey;
        } catch (Exception e) {
            logger.error("S3文件上传失败 - 配置ID: {}, 存储桶: {}, 对象键: {}, 错误信息: {}", 
                        config.getId(), config.getBucket(), fullObjectKey, e.getMessage(), e);
            return null;
        } finally {
            s3Client.close();
        }
    }

    @Override
    public String getFileUrl(S3Config config, String objectKey, Integer expireTime) {
        S3Client s3Client = createS3Client(config);

        try {
            GetUrlRequest getUrlRequest = GetUrlRequest.builder()
                    .bucket(config.getBucket())
                    .key(objectKey)
                    .build();

            return s3Client.utilities().getUrl(getUrlRequest).toString();
        } finally {
            s3Client.close();
        }
    }

    @Override
    public boolean testConnection(S3Config config) {
        S3Client s3Client = createS3Client(config);

        try {
            // 尝试列出存储桶中的对象来测试连接
            ListObjectsV2Request listObjectsRequest = ListObjectsV2Request.builder()
                    .bucket(config.getBucket())
                    .maxKeys(1)
                    .build();

            s3Client.listObjectsV2(listObjectsRequest);
            return true;
        } catch (Exception e) {
            return false;
        } finally {
            s3Client.close();
        }
    }

    @Override
    public boolean deleteFile(S3Config config, String objectKey) {
        S3Client s3Client = createS3Client(config);

        try {
            DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
                    .bucket(config.getBucket())
                    .key(objectKey)
                    .build();

            s3Client.deleteObject(deleteObjectRequest);
            return true;
        } catch (Exception e) {
            return false;
        } finally {
            s3Client.close();
        }
    }

    @Override
    public InputStream downloadFile(S3Config config, String objectKey) {
        logger.info("开始从S3下载文件流 - 配置ID: {}, 存储桶: {}, 对象键: {}", 
                   config.getId(), config.getBucket(), objectKey);
        
        S3Client s3Client = createS3Client(config);
        try {
            GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                    .bucket(config.getBucket())
                    .key(objectKey)
                    .build();
            
            logger.info("构建GetObjectRequest完成 - 存储桶: {}, 对象键: {}", config.getBucket(), objectKey);
            
            InputStream inputStream = s3Client.getObject(getObjectRequest);
            logger.info("S3文件流获取完成 - 存储桶: {}, 对象键: {}", config.getBucket(), objectKey);
            return inputStream;
        } catch (Exception e) {
            logger.error("从S3下载文件失败 - 配置ID: {}, 存储桶: {}, 对象键: {}, 错误信息: {}", 
                        config.getId(), config.getBucket(), objectKey, e.getMessage(), e);
            s3Client.close();
            throw new RuntimeException("从S3下载文件失败: " + e.getMessage(), e);
        }
    }

    @Override
    public byte[] downloadFileAsBytes(S3Config config, String objectKey) {
        logger.info("开始从S3下载文件为字节数组 - 配置ID: {}, 存储桶: {}, 对象键: {}", 
                   config.getId(), config.getBucket(), objectKey);
        
        S3Client s3Client = createS3Client(config);
        try {
            GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                    .bucket(config.getBucket())
                    .key(objectKey)
                    .build();
            
            logger.info("构建GetObjectRequest完成 - 存储桶: {}, 对象键: {}", config.getBucket(), objectKey);

            try (InputStream inputStream = s3Client.getObject(getObjectRequest)) {
                logger.info("开始读取S3文件流 - 存储桶: {}, 对象键: {}", config.getBucket(), objectKey);
                byte[] bytes = inputStream.readAllBytes();
                logger.info("S3文件读取完成 - 存储桶: {}, 对象键: {}, 文件大小: {} 字节", 
                           config.getBucket(), objectKey, bytes.length);
                return bytes;
            }
        } catch (Exception e) {
            logger.error("从S3下载文件失败 - 配置ID: {}, 存储桶: {}, 对象键: {}, 错误信息: {}", 
                        config.getId(), config.getBucket(), objectKey, e.getMessage(), e);
            throw new RuntimeException("从S3下载文件失败: " + e.getMessage(), e);
        } finally {
            logger.info("关闭S3客户端 - 配置ID: {}, 存储桶: {}", config.getId(), config.getBucket());
            s3Client.close();
        }
    }

    /**
     * 创建S3客户端
     *
     * @param config S3配置
     * @return S3客户端
     */
    private S3Client createS3Client(S3Config config) {
        logger.info("开始创建S3客户端 - 配置ID: {}, 端点: {}, 存储桶: {}, 路径风格: {}", 
                   config.getId(), config.getEndpoint(), config.getBucket(), config.getPathStyle());
        
        AwsBasicCredentials awsCreds = AwsBasicCredentials.create(
                config.getAccessKey(),
                config.getSecretKey()
        );

        S3ClientBuilder builder = S3Client.builder()
                .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
                .region(Region.of(config.getRegion() != null ? config.getRegion() : "us-east-1"));
        
        logger.info("设置S3客户端基本配置 - 区域: {}", config.getRegion() != null ? config.getRegion() : "us-east-1");

        // 如果指定了端点，则使用自定义端点
        if (config.getEndpoint() != null && !config.getEndpoint().isEmpty()) {
            logger.info("设置自定义端点 - 端点: {}", config.getEndpoint());
            builder.endpointOverride(URI.create(config.getEndpoint()));
        }

        // 设置路径风格
        if (config.getPathStyle() != null) {
            logger.info("设置路径风格 - 使用路径风格: {}", config.getPathStyle());
            builder.serviceConfiguration(s -> s.pathStyleAccessEnabled(config.getPathStyle()));
        }

        S3Client s3Client = builder.build();
        logger.info("S3客户端创建完成 - 配置ID: {}", config.getId());
        return s3Client;
    }

    /**
     * 构建带基础路径的完整对象键
     *
     * @param config S3配置
     * @param objectKey 对象键
     * @return 完整对象键
     */
    public String buildFullObjectKey(S3Config config, String objectKey) {
        if (config.getBasePath() != null && !config.getBasePath().isEmpty()) {
            // 确保基础路径以斜杠结尾
            String basePath = config.getBasePath();
            if (!basePath.endsWith("/")) {
                basePath += "/";
            }
            return basePath + objectKey;
        }
        return objectKey;
    }

    /**
     * 构建文件访问URL
     *
     * @param config S3配置
     * @param objectKey 对象键
     * @return 文件访问URL
     */
    private String buildFileUrl(S3Config config, String objectKey) {
        if (config.getEndpoint() != null && !config.getEndpoint().isEmpty()) {
            // 自定义端点
            return String.format("%s/%s/%s", config.getEndpoint(), config.getBucket(), objectKey);
        } else {
            // AWS S3标准端点
            return String.format("https://%s.s3.%s.amazonaws.com/%s",
                    config.getBucket(), config.getRegion(), objectKey);
        }
    }
}